package com.dl.study.config.shiroconfig;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dl.study.config.jwt.JwtToken;
import com.dl.study.config.jwt.JwtUtil;
import com.dl.study.config.redis.RedisConstant;
import com.dl.study.config.redis.RedisUtil;
import com.dl.study.module.base.authority.dao.AuthorityDao;
import com.dl.study.module.base.roleinfo.dao.RoleInfoDao;
import com.dl.study.module.user.dao.UserDao;
import com.dl.study.module.user.domain.entity.UserEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;


/**
 * @Author: user
 * @Date: 2021/10/22 10:49
 * @Description:
 */
@Slf4j
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserDao userDao;
    @Autowired
    private RedisUtil redis;

    @Autowired
    private RoleInfoDao roleInfoDao;

    @Autowired
    private AuthorityDao authorityDao;

    /**
     * 大坑，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 用户验证
     * 只有当需要检测用户权限的时候才会调用此方法，例如checkRole,checkPermission之类的
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        String account = JwtUtil.getClaim(principals.toString(), RedisConstant.ACCOUNT);
        ///获取用户角色
        List<String> roles = roleInfoDao.selectRoleByLoginName(account);
        authorizationInfo.addRoles(roles);
        List<String> authoritys = authorityDao.selectAuthorityByLoginName(account);
        authorizationInfo.addStringPermissions(authoritys);
        return authorizationInfo;
    }

    /**
     * 用户鉴权
     *
     * @param auth
     * @return
     * @throws AuthenticationException
     */

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getPrincipal();
        Object obj = auth.getCredentials();
//        String password = new String((char[]) obj);
        String account = JwtUtil.getClaim(token, RedisConstant.ACCOUNT);

        log.info("用户" + account + "认证-----ShiroRealm.doGetAuthenticationInfo");
        QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(UserEntity::getLoginName, account);
        UserEntity user = userDao.selectOne(wrapper);
//
        if (StringUtils.isBlank(account)) {
            throw new AuthenticationException("token invalid");
        }

        if (user == null) {
            throw new AuthenticationException("user didn't existed!");
        }

//        if (!password.equals(user.getLoginPwd())) {
//            throw new IncorrectCredentialsException("用户名或密码错误！");
//        }
//        if (user.getStatus().equals("0")) {
//            throw new LockedAccountException("账号已被锁定,请联系管理员！");
//        }
        // SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(null, password, getName());

        // 开始认证，要AccessToken认证通过，且Redis中存在RefreshToken，且两个Token时间戳一致
        if (JwtUtil.verify(token) && redis.hasKey(RedisConstant.PREFIX_SHIRO_REFRESH_TOKEN + account)) {
            // 获取RefreshToken的时间戳
            String currentTimeMillisRedis = redis.get(RedisConstant.PREFIX_SHIRO_REFRESH_TOKEN + account).toString();
            // 获取AccessToken时间戳，与RefreshToken的时间戳对比
            if (JwtUtil.getClaim(token, RedisConstant.CURRENT_TIME_MILLIS).equals(currentTimeMillisRedis)) {
                String userRealm = this.getName();
                return new SimpleAuthenticationInfo(token, token, userRealm);
            }
        }
        throw new AuthenticationException("Token已过期(Token expired or incorrect.)");

        //  return info;
    }
}
